#include "SerialPortManager.hpp"
#include <iostream>
// to detect devices
#include <windows.h>
#include <devguid.h>
// library: Setupapi.lib
#include <setupapi.h>

#include "../../MiscUtils/TdrMisc/Headers/tFormatter.hpp"
#include "../../MiscUtils/TdrMisc/Headers/tLogger.hpp"
#include "../../MiscUtils/TdrMisc/Headers/tTime.hpp"

#include <comdef.h>
#include <Wbemidl.h>

//#define DEV_PART

//#define SERIAL_PORT_MANAGER_DEBUG_SEND_LOG
//#define SERIAL_PORT_MANAGER_DEBUG_RECEIVE_LOG

using namespace std;

U8Vec subVecBefore(U8Vec vec, uint8_t c) {
	U8Vec ret;
	for (uint8_t ele : vec) {
		if (ele != c) {
			ret.push_back(ele);
		}
		else {
			return ret;
		}
	}
	return U8Vec();
}

U8Vec subVecAfter(U8Vec vec, uint8_t c) {
	for (int idx = vec.size() - 1; idx >= 0; idx--) {
		if (vec[idx] == c) {
			U8Vec ret;
			for (idx++; idx < vec.size(); idx++) {
				ret.push_back(vec[idx]);
			}
			return ret;
		}
	}
	return U8Vec();
}

void u8append(U8Vec& vec0, void* vec1, int vec1Len) {
	if (vec1Len < 0) {
		return;
	}
	int oldLen = vec0.size();
	vec0.resize(oldLen + vec1Len);
	memcpy(&vec0[oldLen], vec1, vec1Len);
}

class SerialUtilsOnPlatform {
public:
	static std::string hasError(HANDLE handle);
};

string SerialUtilsOnPlatform::hasError(HANDLE handle) {
	if (handle == NULL) {
		return "";
	}
	DWORD dwErrors = 0;
	COMSTAT comStat;
	if (ClearCommError(handle, &dwErrors, &comStat) > 0) {
		// Report error in ClearCommError.
		return "";
	}

	// Get error flags.
	BOOL    fDNS = dwErrors & CE_DNS;
	BOOL    fIOE = dwErrors & CE_IOE;
	BOOL    fOOP = dwErrors & CE_OOP;
	BOOL    fPTO = dwErrors & CE_PTO;
	BOOL    fMODE = dwErrors & CE_MODE;
	BOOL    fBREAK = dwErrors & CE_BREAK;
	BOOL    fFRAME = dwErrors & CE_FRAME;
	BOOL    fRXOVER = dwErrors & CE_RXOVER;
	BOOL    fTXFULL = dwErrors & CE_TXFULL;
	BOOL    fOVERRUN = dwErrors & CE_OVERRUN;
	BOOL    fRXPARITY = dwErrors & CE_RXPARITY;
	BOOL opened = (dwErrors == IE_OPEN);
	return "Unspecified";
}

std::wstring getAttribute(HDEVINFO& hDeviceInfo, SP_DEVINFO_DATA& devInfoData, unsigned int code) {
	DWORD regDataType;
	DWORD reqSize = 0;

	SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, code, nullptr, nullptr, 0, &reqSize);
	BYTE* codeBuf = new BYTE[(reqSize > 1) ? reqSize : 1];
	wstring codeWStr;
	if (SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, code, &regDataType, codeBuf, sizeof(codeBuf) * reqSize, nullptr))
	{
		if (reqSize > 0) {
			codeWStr.resize(reqSize);
			memcpy(&codeWStr[0], codeBuf, reqSize);
		}
	}
	delete[] codeBuf;
	return codeWStr;
}

int SerialPortManager::HandShakeProtocolSerial::excute(SerialPortManager* manager) {
	for (int idx = 0; idx < handshakes.size(); idx++) {
		bool flagShakeSuccess = false;
		for (int trialCount = 0; (trialCount < handshakes[idx].toSend.retryNum) && (!flagShakeSuccess); trialCount++) {
			// send, optional
			if (handshakes[idx].toSend.dataToSend.size() > 0) {
				if (!manager->send(&handshakes[idx].toSend.dataToSend[0], handshakes[idx].toSend.dataToSend.size())) {
					return -idx;
				}
			}

			if (handshakes[idx].exp.possibleReplies.size() > 0) {
				// just one reply implemented
				int posIdx = 0;
				U8Vec reply;
				if (handshakes[idx].exp.isReplyTemplate[0] > 0) {
					U8Vec header = subVecBefore(handshakes[idx].exp.possibleReplies[posIdx], '{');
					U8Vec tail = subVecAfter(handshakes[idx].exp.possibleReplies[posIdx], '}');
					reply = manager->receiveBytes(
						"", header,
						"", tail,
						handshakes[idx].exp.timeout);

					vector<U8Vec> debugInter, idxStr;
					idxStr = TFormater::splitVars(reply, '{', '}', &debugInter);
					if (idxStr.size() == 1) {
						int sameNameIdx = TFormater::u82int(&(idxStr[0][0]), idxStr[0].size());
						handshakes[idx].result.parsedNumberOut.push_back(sameNameIdx);
						flagShakeSuccess = true;
					}
				}
				else {
					if (manager->checkReplyU8(handshakes[idx].exp.possibleReplies[0], handshakes[idx].exp.timeout, &reply)) {
						flagShakeSuccess = true;
					}
				}
				if (flagShakeSuccess) {
					handshakes[idx].result.replyTypeIdxOut = posIdx;
				}
				handshakes[idx].result.reply = reply;
			}
		}
		if (!flagShakeSuccess) {
			return -idx;
		}
		manager->purge();
	}
	return 1;
}

SerialPortManager::SerialPortManager(void* handle) {
	_sCommCfg.serialHandle = handle;
	_sCommCfg.isConfiged = 0;
}

SerialPortManager::~SerialPortManager() {
	if (_sCommCfg.isHandleShallowCopy <= 0) {
		CloseHandle(_sCommCfg.serialHandle);
	}
}

bool SerialPortManager::isSimu() {
	return _sCommCfg.flagSimu;
}

bool SerialPortManager::configSimu(bool flagSimu) {
	_sCommCfg.flagSimu = flagSimu;
	return true;
}

bool SerialPortManager::makeShallowCopy(SerialPortManager* refManager) {
	_sCommCfg = refManager->_sCommCfg;
	_sCommCfg.isHandleShallowCopy = 1;
	return true;
}

vector<SerialPortManager*> SerialPortManager::listPortsWithHardwareIds(std::vector<std::wstring>& hwIds) {
	vector<SerialPortManager*> ret;

	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		//writeWarnning("INVALID_HANDLE_VALUE");
		return ret;
	}

	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		for (wstring hwIdWStr : hwIds) {
			if (hardwareID.find(hwIdWStr) != std::wstring::npos) {
				wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
				int begin = friendlyName.find_last_of(L"(");
				int end = friendlyName.find_last_of(L")");
				std::wstring portName = friendlyName.substr(begin + 1, end - begin - 1);

				portName = L"\\\\.\\" + portName;
				HANDLE serialHandle = CreateFile(
					portName.c_str(),
					GENERIC_READ | GENERIC_WRITE,
					FILE_SHARE_READ | FILE_SHARE_WRITE,
					NULL, OPEN_EXISTING,
					0,
					NULL);

				string err = SerialUtilsOnPlatform::hasError(serialHandle);
				if (err.length() != 0) {
					CloseHandle(serialHandle);
				}
				else {
					SerialPortManager* newPort = new SerialPortManager(serialHandle);
					newPort->_sCommCfg.portName = portName;
					ret.push_back(newPort);
				}
			}
		}
	}

	return ret;
}

void SerialPortManager::resetUSBDevices() {
	// soft reboot through dtr pin
	TLogger block("Reset USB devices");
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		//writeWarnning("INVALID_HANDLE_VALUE");
	}

	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
		auto begin = friendlyName.find_last_of(L"(");
		wstring name = friendlyName.substr(begin + 1);
		if (!TWFormater::beginWith(name, L"COM")) {
			continue;
		}
		auto end = name.find_last_of(L")");
		std::wstring portName = name.substr(0, end);
		wstring portNameMain = L"\\\\.\\" + portName;
		HANDLE serialHandle = CreateFile(
			portNameMain.c_str(),
			GENERIC_READ | GENERIC_WRITE,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL, OPEN_EXISTING,
			0,
			NULL);

		if (serialHandle == NULL) {
			TLogger::writeLine(L"Cannot open " + portName);
			continue;
		}

		DCB serialParams = { 0 };
		serialParams.DCBlength = sizeof(serialParams);
		if (GetCommState(serialHandle, &serialParams) == 0) {
			CloseHandle(serialHandle);
			continue;
		}

		serialParams.BaudRate = CBR_115200;
		serialParams.ByteSize = 8;
		serialParams.StopBits = ONESTOPBIT;
		serialParams.Parity = NOPARITY;
		if (SetCommState(serialHandle, &serialParams) == 0) {
			CloseHandle(serialHandle);
			continue;
		}


		TLogger::writeLine(L"Reset port " + portName);
		SerialPortManager* newPort = new SerialPortManager(serialHandle);
		newPort->setDtr(0);
		Sleep(100);
		newPort->setDtr(1);
		Sleep(100);
		auto data = newPort->receiveAnyBytes();
		if (data.size() > 64) {
			data.resize(64);
		}
		auto datStr = TFormater::toDisplayable(data);
		TLogger::writeLine("Data after reboot: " + datStr);
		delete newPort;
		TLogger::writeLine(L"Done");
	}
}

bool SerialPortManager::connectHWID(std::wstring hardwareIDIn, int boudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn, int portIdx) {
	if (_sCommCfg.serialHandle != NULL) {
		return true;
	}

	errorHint = "";
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		errorHint = "Cannot get port list";
		return false;
	}

	// iterate over all the devices in the tree
	_sCommCfg.portName = L"";
	int nDevice = 0;
	bool found = false;
	int portCount = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData) && (!found))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		if (hardwareID.find(hardwareIDIn) != std::wstring::npos) {
			if (portCount++ == portIdx) {
				wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
				int begin = friendlyName.find_first_of(L"(");
				int end = friendlyName.find_first_of(L")");
				_sCommCfg.portName = friendlyName.substr(begin + 1, end - begin - 1);
				found = true;
			}
		}
	}
	if (!found) {
		errorHint = "Cannot find port";
		return false;
	}

	_sCommCfg.portName = L"\\\\.\\" + _sCommCfg.portName;
	_sCommCfg.baudRate = boudRateIn;
	_sCommCfg.parity = parityIn;
	_sCommCfg.dataBitNum = dataBitNumIn;
	_sCommCfg.stopBitNum = stopBitsIn;

	return connectToSelectedPortCore() > 0;
}

bool SerialPortManager::connectHWIDWithHandshake(std::wstring hardwareIDIn,
	U8Vec content, U8Vec expectedReply,
	int waitAfterOpen, int handshakeTimeout,
	int baudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn) {
	if (_sCommCfg.serialHandle != NULL) {
		return true;
	}

	errorHint = "";
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		errorHint.append("Cannot get port list");
		return false;
	}

	// iterate over all the devices in the tree
	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		if (hardwareID.find(hardwareIDIn) != std::wstring::npos) {
			wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
			int begin = friendlyName.find_first_of(L"(");
			int end = friendlyName.find_first_of(L")");
			wstring tempPortName = friendlyName.substr(begin + 1, end - begin - 1);
			tempPortName = L"\\\\.\\" + tempPortName;

			HANDLE tempHandle = CreateFile(
				tempPortName.c_str(),
				GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				NULL, OPEN_EXISTING,
				0,
				NULL);

			string portNameStr = TFormater::wstring2string(tempPortName);
			if (tempHandle == NULL) {
				string err = SerialUtilsOnPlatform::hasError(tempHandle);
				errorHint.append("Found device matching HWID on port " + portNameStr + " but cannot connect. Hint: {" + err + "}");
				continue;
			}

			DCB serialParams = { 0 };
			serialParams.DCBlength = sizeof(serialParams);

			if (GetCommState(tempHandle, &serialParams) == 0) {
				errorHint.append("Cannot get comm state.");
				CloseHandle(tempHandle);
				continue;
			}

			if (baudRateIn == 115200) {
				serialParams.BaudRate = CBR_115200;
			}
			if (dataBitNumIn == 8) {
				serialParams.ByteSize = 8;
			}
			serialParams.StopBits = ONESTOPBIT;
			if (parityIn == 'n' || parityIn == 'N') {
				serialParams.Parity = NOPARITY;
			}
			if (SetCommState(tempHandle, &serialParams) == 0) {
				errorHint.append("Cannot set comm state.");
				CloseHandle(tempHandle);
				continue;
			}

			// Set timeouts
			COMMTIMEOUTS timeout = { 0 };
			timeout.ReadIntervalTimeout = 5;
			timeout.ReadTotalTimeoutConstant = 5;
			timeout.ReadTotalTimeoutMultiplier = 5;
			timeout.WriteTotalTimeoutConstant = 5;
			timeout.WriteTotalTimeoutMultiplier = 5;

			if (SetCommTimeouts(tempHandle, &timeout) == 0) {
				errorHint.append("Cannot set timtouts.");
				CloseHandle(tempHandle);
				continue;
			}

			_sCommCfg.serialHandle = tempHandle;
			receiveBuffer.clear();
			if (waitAfterOpen > 0) {
				Sleep(waitAfterOpen);
			}

			U8Vec reply;

			for (int idx = 0; idx < 3; idx++) {
				if (send(content)) {
					if (checkReplyU8(expectedReply, handshakeTimeout, &reply)) {
						_sCommCfg.baudRate = baudRateIn;
						_sCommCfg.parity = parityIn;
						_sCommCfg.dataBitNum = dataBitNumIn;
						_sCommCfg.stopBitNum = stopBitsIn;

						_sCommCfg.portName = tempPortName;

						return true;
					}
					else {
						errorHint.append("Cannot send.");
					}
				}

				Sleep(waitAfterOpen);
				purge();
			}
			errorHint.append("Handshake fail. S{" + TFormater::toDisplayable(content) + "}R{" + TFormater::toDisplayable(reply) + "}");
			CloseHandle(tempHandle);
			_sCommCfg.serialHandle = NULL;
		}
	}

	return false;
}

int SerialPortManager::connectHWIDWithHandshakeOfIndex(std::wstring hardwareIDIn,
	U8Vec content, U8Vec expectedReply,
	int waitAfterOpen, int handshakeTimeout,
	int baudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn) {
	if (_sCommCfg.serialHandle != NULL) {
		return _sCommCfg.sameNameIdx;
	}

	errorHint = "";
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		errorHint.append("Cannot get port list");
		return -1;
	}

	// iterate over all the devices in the tree
	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		if (hardwareID.find(hardwareIDIn) != std::wstring::npos) {
			wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
			int begin = friendlyName.find_first_of(L"(");
			int end = friendlyName.find_first_of(L")");
			wstring tempPortName = friendlyName.substr(begin + 1, end - begin - 1);
			tempPortName = L"\\\\.\\" + tempPortName;

			HANDLE tempHandle = CreateFile(
				tempPortName.c_str(),
				GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				NULL, OPEN_EXISTING,
				0,
				NULL);

			string portNameStr = TFormater::wstring2string(tempPortName);
			if (tempHandle == NULL) {
				string err = SerialUtilsOnPlatform::hasError(tempHandle);
				errorHint.append("Found device matching HWID on port " + portNameStr + " but cannot connect. Hint: {" + err + "}");
				continue;
			}

			DCB serialParams = { 0 };
			serialParams.DCBlength = sizeof(serialParams);

			if (GetCommState(tempHandle, &serialParams) == 0) {
				errorHint.append("Cannot get comm state.");
				CloseHandle(tempHandle);
				continue;
			}

			if (baudRateIn == 115200) {
				serialParams.BaudRate = CBR_115200;
			}
			if (dataBitNumIn == 8) {
				serialParams.ByteSize = 8;
			}
			serialParams.StopBits = ONESTOPBIT;
			if (parityIn == 'n' || parityIn == 'N') {
				serialParams.Parity = NOPARITY;
			}
			if (SetCommState(tempHandle, &serialParams) == 0) {
				errorHint.append("Cannot set comm state.");
				CloseHandle(tempHandle);
				continue;
			}

			// Set timeouts
			COMMTIMEOUTS timeout = { 0 };
			timeout.ReadIntervalTimeout = 5;
			timeout.ReadTotalTimeoutConstant = 5;
			timeout.ReadTotalTimeoutMultiplier = 5;
			timeout.WriteTotalTimeoutConstant = 5;
			timeout.WriteTotalTimeoutMultiplier = 5;

			if (SetCommTimeouts(tempHandle, &timeout) == 0) {
				errorHint.append("Cannot set timtouts.");
				CloseHandle(tempHandle);
				continue;
			}

			_sCommCfg.serialHandle = tempHandle;
			receiveBuffer.clear();
			if (waitAfterOpen > 0) {
				Sleep(waitAfterOpen);
			}
			vector<U8Vec> debugInter, idxStr;
			if (send(content)) {
				//&& checkReplyU8(expectedReply, handshakeTimeout)) {
				U8Vec reply = receiveBytes(
					"", subVecBefore(expectedReply, '{'),
					"", subVecAfter(expectedReply, '}'),
					handshakeTimeout);
				if (receiveBuffer.size() != 0) {
					idxStr = TFormater::splitVars(reply, '{', '}', &debugInter);
					if (idxStr.size() == 0) {
						purge();
						send(content);
						reply = receiveBytes(
							"", subVecBefore(expectedReply, '{'),
							"", subVecAfter(expectedReply, '}'),
							handshakeTimeout);
						idxStr = TFormater::splitVars(reply, '{', '}', &debugInter);
					}
				}
				if (idxStr.size() == 1) {
					_sCommCfg.sameNameIdx = TFormater::u82int(&(idxStr[0][0]), idxStr[0].size());
					_sCommCfg.baudRate = baudRateIn;
					_sCommCfg.parity = parityIn;
					_sCommCfg.dataBitNum = dataBitNumIn;
					_sCommCfg.stopBitNum = stopBitsIn;

					_sCommCfg.portName = tempPortName;

					return _sCommCfg.sameNameIdx;
				}
				else {
					return -1;
				}
			}
			else {
				errorHint.append("Handshake fail.");
				CloseHandle(tempHandle);
				_sCommCfg.serialHandle = NULL;
			}
		}
	}

	return -1;
}

HANDLE openPortCore(HDEVINFO& hDeviceInfo, SP_DEVINFO_DATA& devInfoData,
	int baudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn, string* errorHintInOut, wstring* portNameOut) {

	wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
	int begin = friendlyName.find_first_of(L"(");
	int end = friendlyName.find_first_of(L")");
	wstring tempPortName = friendlyName.substr(begin + 1, end - begin - 1);
	tempPortName = L"\\\\.\\" + tempPortName;

	HANDLE tempHandle = CreateFile(
		tempPortName.c_str(),
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING,
		0,
		NULL);

	string portNameStr = TFormater::wstring2string(tempPortName);
	if (tempHandle == NULL) {
		//|| SerialUtilsOnPlatform::hasError(tempHandle)) {
		string err = SerialUtilsOnPlatform::hasError(tempHandle);
		errorHintInOut->append("Found device matching HWID on port " + portNameStr + " but cannot connect. Hint: {" + err + "}");
		//*errorHintInOut.append("Found device matching HWID on port " + portNameStr + " but cannot connect. Buzy?";
		return NULL;
	}

	DCB serialParams = { 0 };
	serialParams.DCBlength = sizeof(serialParams);

	if (GetCommState(tempHandle, &serialParams) == 0) {
		errorHintInOut->append("Cannot get comm state.");
		CloseHandle(tempHandle);
		return NULL;
	}

	if (baudRateIn == 115200) {
		serialParams.BaudRate = CBR_115200;
	}
	if (dataBitNumIn == 8) {
		serialParams.ByteSize = 8;
	}
	serialParams.StopBits = ONESTOPBIT;
	if (parityIn == 'n' || parityIn == 'N') {
		serialParams.Parity = NOPARITY;
	}
	if (SetCommState(tempHandle, &serialParams) == 0) {
		errorHintInOut->append("Cannot set comm state.");
		CloseHandle(tempHandle);
		return NULL;
	}

	// Set timeouts
	COMMTIMEOUTS timeout = { 0 };
	timeout.ReadIntervalTimeout = 5;
	timeout.ReadTotalTimeoutConstant = 5;
	timeout.ReadTotalTimeoutMultiplier = 5;
	timeout.WriteTotalTimeoutConstant = 5;
	timeout.WriteTotalTimeoutMultiplier = 5;

	if (SetCommTimeouts(tempHandle, &timeout) == 0) {
		errorHintInOut->append("Cannot set timtouts.");
		CloseHandle(tempHandle);
		return NULL;
	}

	if (portNameOut != nullptr) {
		*portNameOut = tempPortName;
	}
	return tempHandle;
}

int SerialPortManager::connectHWIDWithHandshake(std::wstring hardwareIDIn,
	int waitAfterOpen,
	HandShakeProtocolSerial& handshake,
	int baudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn) {
	if (_sCommCfg.serialHandle != NULL) {
		return _sCommCfg.sameNameIdx;
	}

	errorHint = "";
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		errorHint.append("Cannot get port list");
		return -1;
	}

	// iterate over all the devices in the tree
	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		if (hardwareID.find(hardwareIDIn) != std::wstring::npos) {

			wstring portNameTmp;
			_sCommCfg.serialHandle = openPortCore(hDeviceInfo, devInfoData, baudRateIn, parityIn, dataBitNumIn, stopBitsIn, &errorHint, &portNameTmp);
			if (_sCommCfg.serialHandle == NULL) {
				continue;
			}

			receiveBuffer.clear();
			if (waitAfterOpen > 0) {
				Sleep(waitAfterOpen);
			}

			if (handshake.excute(this) > 0) {
				_sCommCfg.baudRate = baudRateIn;;
				_sCommCfg.dataBitNum = dataBitNumIn;
				_sCommCfg.stopBitNum = stopBitsIn;
				_sCommCfg.parity = parityIn;
				_sCommCfg.portName = portNameTmp;
				_sCommCfg.handshake = handshake;

				//for (int idx = 0; idx < handshake.handshakes.size(); idx++) {
				//	auto& shk = handshake.handshakes[idx];

				//	_sentCallback(true,
				//		shk.toSend.dataToSend.data(), shk.toSend.dataToSend.size(), nullptr, 0);

				//	_receiveCallback(shk.result.replyTypeIdxOut >= 0,
				//		shk.result.reply.data(), shk.result.reply.size(),
				//		receiveBuffer.data(), receiveBuffer.size());

				//	if (shk.result.replyTypeIdxOut < 0) {
				//		break;
				//	}
				//}

				return 1;
			}
			else {
				errorHint.append("Handshake fail.");
				CloseHandle(_sCommCfg.serialHandle);
				_sCommCfg.serialHandle = NULL;
				receiveBuffer.clear();
			}
		}
	}

	return -1;
}

void SerialPortManager::simulateConnect(HandShakeProtocol& handshake,
	bool simu, int baudRate, char parity, int dataBitNum, int stopBits) {
	_sCommCfg.flagSimu = simu;
	_sCommCfg.baudRate = baudRate;
	_sCommCfg.dataBitNum = dataBitNum;
	_sCommCfg.stopBitNum = stopBits;
	_sCommCfg.parity = parity;
	_sCommCfg.portName = L"SimuCOMM";

	simulateHandshake(handshake);
}

void SerialPortManager::simulateHandshake(HandShakeProtocol& handshake) {
	string report;
	report += "[CommConfig] " + _sCommCfg.toStr() + "\n";
	report += "[Handshakes]\n" + handshake.toStr(2);
	TLogger::writeLine(report);

	for (auto& shake : handshake.handshakes) {
		if (shake.toSend.dataToSend.size() > 0) {
			_sentCallback(true, shake.toSend.dataToSend.data(), shake.toSend.dataToSend.size(), nullptr, 0);
		}
		if (shake.exp.possibleReplies.size() > 0) {
			_receiveCallback(true, shake.exp.possibleReplies[0].data(), shake.exp.possibleReplies[0].size(), nullptr, 0);
		}
	}
}

bool SerialPortManager::isConnected() {
	if (_sCommCfg.flagSimu) {
		return _sCommCfg.portName.length() > 0;
	}
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}

	// not sure
	DCB serialParams = { 0 };
	serialParams.DCBlength = sizeof(serialParams);

	if (GetCommState(_sCommCfg.serialHandle, &serialParams) == 0) {
		disconnect();
		return false;
	}
	else {
		return true;
	}
}

bool SerialPortManager::rebootDriver() {
	// by doubao querry: reboot serial port device in c++ through wmi
#ifdef DEV_PART
	// Initializing COM
	HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres))
	{
		std::cerr << "Failed to initialize COM library. Error code = " << hres << std::endl;
		return 1;
	}

	IWbemLocator* pLoc = NULL;
	hres = CoCreateInstance(
		CLSID_WbemLocator,
		0,
		CLSCTX_INPROC_SERVER,
		IID_IWbemLocator,
		(LPVOID*)&pLoc);
	if (FAILED(hres))
	{
		std::cerr << "Failed to create IWbemLocator object. Error code = " << hres << std::endl;
		CoUninitialize();
		return 1;
	}
	IWbemServices* pSvc = NULL;
	hres = pLoc->ConnectServer(
		_bstr_t(L"ROOT\\cimv2"),
		NULL,
		NULL,
		0,
		NULL,
		0,
		0,
		&pSvc);
	if (FAILED(hres))
	{
		std::cerr << "Could not connect to WMI namespace. Error code = " << hres << std::endl;
		pLoc->Release();
		CoUninitialize();
		return 1;
	}
#endif
	return false;
}

#include <cfgmgr32.h>
BOOL DisableCOMPortDevice(HDEVINFO hDevInfo, SP_DEVINFO_DATA& devInfoData) {
	// Get the Device Instance ID
	TCHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
	if (SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, szDeviceInstanceID, MAX_DEVICE_ID_LEN, NULL)) {
		// Disable the device by using CM_Disable_DevNode
		DEVNODE devNode;
		if (CM_Locate_DevNode(&devNode, szDeviceInstanceID, 0) == CR_SUCCESS) {
			return CM_Disable_DevNode(devNode, 0) == CR_SUCCESS;
		}
	}
	return FALSE;
}

BOOL EnableCOMPortDevice(HDEVINFO hDevInfo, SP_DEVINFO_DATA& devInfoData) {
	// Get the Device Instance ID
	TCHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
	if (SetupDiGetDeviceInstanceId(hDevInfo, &devInfoData, szDeviceInstanceID, MAX_DEVICE_ID_LEN, NULL)) {
		// Enable the device by using CM_Enable_DevNode
		DEVNODE devNode;
		if (CM_Locate_DevNode(&devNode, szDeviceInstanceID, 0) == CR_SUCCESS) {
			return CM_Enable_DevNode(devNode, 0) == CR_SUCCESS;
		}
	}
	return FALSE;
}

bool SerialPortManager::rebootDrivers() {
	SP_DEVINFO_DATA devInfoData = {};
	devInfoData.cbSize = sizeof(devInfoData);

	// get the tree containing the info for the ports
	HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
		0,
		nullptr,
		DIGCF_PRESENT
	);
	if (hDeviceInfo == INVALID_HANDLE_VALUE)
	{
		//errorHint.append("Cannot get port list");
		return false;
	}

	// iterate over all the devices in the tree
	int nDevice = 0;
	while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
		nDevice++,            // The member to look for
		&devInfoData))
	{
		wstring hardwareID = getAttribute(hDeviceInfo, devInfoData, SPDRP_HARDWAREID);
		{
			wstring friendlyName = getAttribute(hDeviceInfo, devInfoData, SPDRP_FRIENDLYNAME);
			auto begin = friendlyName.find_first_of(L"(");
			auto end = friendlyName.find_first_of(L")");
			if (begin != wstring::npos && end != wstring::npos) {
				wstring tempPortName = friendlyName.substr(begin + 1, end - begin - 1);
				if (tempPortName.find(L"COM") != wstring::npos) {
					DisableCOMPortDevice(hDeviceInfo, devInfoData);
					EnableCOMPortDevice(hDeviceInfo, devInfoData);
				}
			}
		}
	}

}
bool SerialPortManager::config(int baudRateIn, char parityIn, int dataBitNumIn, int stopBitsIn) {
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}

	_sCommCfg.baudRate = baudRateIn;
	_sCommCfg.parity = parityIn;
	_sCommCfg.dataBitNum = dataBitNumIn;
	_sCommCfg.stopBitNum = stopBitsIn;

	DCB serialParams = { 0 };
	serialParams.DCBlength = sizeof(serialParams);
	if (baudRateIn == 115200) {
		serialParams.BaudRate = CBR_115200;
	}
	if (dataBitNumIn == 8) {
		serialParams.ByteSize = 8;
	}
	serialParams.StopBits = ONESTOPBIT;
	if (parityIn == 'n' || parityIn == 'N') {
		serialParams.Parity = NOPARITY;
	}
	if (SetCommState(_sCommCfg.serialHandle, &serialParams) == 0) {
		errorHint += "Cannot set comm state.";
		return false;
	}

	//setDtr(1);
	//setRts(1);

	return true;
}

std::string SerialPortManager::getPortName() {
	return TFormater::wstring2string(_sCommCfg.portName);
}

string SerialPortManager::PortConfig::toStr() {
	std::string ret;

	ret += "Simu=" + TFormater::int2str(flagSimu) + ";";
	ret += "Configed=" + TFormater::int2str(isConfiged) + ";";
	ret += "BaudRate=" + TFormater::int2str(baudRate) + ";";
	ret += "Parity=" + TFormater::char2str(parity) + ";";
	ret += "DataBit=" + TFormater::int2str(dataBitNum) + ";";
	ret += "StopBit=" + TFormater::int2str(stopBitNum) + ";";

	return ret;
}

std::string SerialPortManager::getConnectionConfig() {
	return _sCommCfg.toStr();
}

bool SerialPortManager::setRts(int val) {
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}
	if (val > 0) {
		return EscapeCommFunction(_sCommCfg.serialHandle, SETRTS);
	}
	else {
		return EscapeCommFunction(_sCommCfg.serialHandle, CLRRTS);
	}
}

bool SerialPortManager::setDtr(int val) {
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}
	if (val > 0) {
		return EscapeCommFunction(_sCommCfg.serialHandle, SETDTR);
	}
	else {
		return EscapeCommFunction(_sCommCfg.serialHandle, CLRDTR);
	}
}

int SerialPortManager::readCts() {
	if (_sCommCfg.serialHandle == NULL) {
		return -1;
	}
	DWORD modemStat;
	GetCommModemStatus(_sCommCfg.serialHandle, &modemStat);
	return modemStat & MS_CTS_ON;
}

int SerialPortManager::readDsr() {
	if (_sCommCfg.serialHandle == NULL) {
		return -1;
	}
	DWORD modemStat;
	GetCommModemStatus(_sCommCfg.serialHandle, &modemStat);
	return modemStat & MS_DSR_ON;
}

bool SerialPortManager::purge() {
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}
	receiveToBuffer();
	if (receiveBuffer.size() > 0) {
		TLogger::writeLine("Purge content {" + TFormater::toDisplayable(receiveBuffer) + "}");
		_receiveCallback(false, receiveBuffer.data(), receiveBuffer.size(), nullptr, 0);
		receiveBuffer.clear();
	}
	return PurgeComm(_sCommCfg.serialHandle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
}

bool SerialPortManager::send(string content) {
	return send(&content[0], content.length());
}

bool SerialPortManager::send(U8Vec content) {
	if (content.size() == 0) {
		return true;
	}
	return send(&content[0], content.size());
}

bool SerialPortManager::send(void* buffer, size_t bufferLen) {
	if (_sCommCfg.flagSimu) {
		_sentCallback(true, buffer, bufferLen, nullptr, 0);
		return true;
	}
	if (_sCommCfg.serialHandle == NULL) {
		return false;
	}
	try {
		bool ret = WriteFile(_sCommCfg.serialHandle, buffer, bufferLen, NULL, NULL) && FlushFileBuffers(_sCommCfg.serialHandle);

		_sentCallback(ret, buffer, bufferLen, nullptr, 0);

		return ret;
	}
	catch (exception e) {

		return false;
	}
}

U8Vec SerialPortManager::peakBytes() {
	if (receiveToBuffer() > 0) {
	}
	return receiveBuffer;
}

std::string SerialPortManager::peakStr(
	std::string replyStartContent,
	std::string replyEndStr, U8Vec replyEndByte,
	int timeOutMS) {
	void* startKeywordPtr = nullptr, * endKeywordPtr = nullptr;
	int startKeywordLen = 0, endKeywordLen = 0;
	int replyStartIdx, replyEndIdx;
	if (replyStartContent.length() > 0) {
		startKeywordPtr = &replyStartContent[0];
		startKeywordLen = replyStartContent.length();
	}
	if (replyEndStr.length() > 0) {
		endKeywordPtr = &replyEndStr[0];
		endKeywordLen = replyEndStr.length();
	}
	else if (replyEndByte.size() > 0) {
		endKeywordPtr = &replyEndByte[0];
		endKeywordLen = replyEndByte.size();
	}

	if (!receiveUntilMatchStartEnd(0, startKeywordPtr, startKeywordLen,
		endKeywordPtr, endKeywordLen, timeOutMS, &replyStartIdx, &replyEndIdx)) {
		return "";
	}
	else {
		return copyFromBuffer(replyStartIdx, replyEndIdx);
	}
}

std::string SerialPortManager::receiveStr(
	std::string replyStartContent,
	std::string replyEndStr, U8Vec replyEndByte,
	int timeOutMS) {
	if (_sCommCfg.flagSimu) {
		U8Vec reply;
		if (replyEndByte.size() > 0) {
			reply = replyEndByte;
		}
		else {
			reply.resize(replyEndStr.size());
			memcpy(&reply[0], replyEndStr.data(), reply.size());
		}
		_receiveCallback(true, reply.data(), reply.size(), nullptr, 0);
		return "";
	}
	void* startKeywordPtr = nullptr, * endKeywordPtr = nullptr;
	int startKeywordLen = 0, endKeywordLen = 0;
	int replyStartIdx, replyEndIdx;
	if (replyStartContent.length() > 0) {
		startKeywordPtr = &replyStartContent[0];
		startKeywordLen = replyStartContent.length();
	}
	if (replyEndStr.length() > 0) {
		endKeywordPtr = &replyEndStr[0];
		endKeywordLen = replyEndStr.length();
	}
	else if (replyEndByte.size() > 0) {
		endKeywordPtr = &replyEndByte[0];
		endKeywordLen = replyEndByte.size();
	}

	if (!receiveUntilMatchStartEnd(0, startKeywordPtr, startKeywordLen,
		endKeywordPtr, endKeywordLen, timeOutMS, &replyStartIdx, &replyEndIdx)) {
			{
				U8Vec reply;
				//TFormater::str2u8(replyStartContent, reply);
				//TFormater::str2u8("*", reply);
				//u8append(reply, endKeywordPtr, endKeywordLen);
				_receiveCallback(false, reply.data(), reply.size(), receiveBuffer.data(), receiveBuffer.size());
			}
			return "";
	}
	else {
		std::string ret = extractFromBuffer(replyStartIdx, replyEndIdx);

		_receiveCallback(true, ret.data(), ret.size(), nullptr, 0);

		return ret;
	}
}

std::string SerialPortManager::receiveStr(
	std::string replyStartContent,
	std::string replyEndStr,
	int timeOutMS) {
	return receiveStr(replyStartContent, replyEndStr, U8Vec(), timeOutMS);
}

void SerialPortManager::simuReceive(std::string reply) {
	_receiveCallback(true, reply.data(), reply.size(), nullptr, 0);
}

U8Vec SerialPortManager::receiveBytes(
	std::string replyStartContent, U8Vec replyStartBytes,
	std::string replyEndStr, U8Vec replyEndByte,
	int timeOutMS) {
	auto start = clock();
	if (!isConnected()) {
		lastReceivedBytes.clear();
		return U8Vec();
	}
	void* startKeywordPtr = nullptr, * endKeywordPtr = nullptr;
	int startKeywordLen = 0, endKeywordLen = 0;
	int replyStartIdx, replyEndIdx;
	if (replyStartContent.length() > 0) {
		startKeywordPtr = &replyStartContent[0];
		startKeywordLen = replyStartContent.length();
	}
	else if (replyStartBytes.size() > 0) {
		startKeywordPtr = &replyStartBytes[0];
		startKeywordLen = replyStartBytes.size();
	}
	if (replyEndStr.length() > 0) {
		endKeywordPtr = &replyEndStr[0];
		endKeywordLen = replyEndStr.length();
	}
	else if (replyEndByte.size() > 0) {
		endKeywordPtr = &replyEndByte[0];
		endKeywordLen = replyEndByte.size();
	}

	if (!receiveUntilMatchStartEnd(0, startKeywordPtr, startKeywordLen,
		endKeywordPtr, endKeywordLen, timeOutMS, &replyStartIdx, &replyEndIdx)) {
		lastReceivedBytes.clear();
	}
	else {
		lastReceivedBytes.resize(replyEndIdx - replyStartIdx + 1);
		memcpy(&lastReceivedBytes[0], &receiveBuffer[replyStartIdx], replyEndIdx - replyStartIdx + 1);
		if (replyEndIdx == receiveBuffer.size() - 1) {
			receiveBuffer.clear();
		}
		else {
			memmove(&receiveBuffer[0], &receiveBuffer[replyEndIdx + 1], receiveBuffer.size() - replyEndIdx - 1);
			receiveBuffer.resize(receiveBuffer.size() - replyEndIdx - 1);
		}
		//SLogger::writeLine("SerialPortManager::receiveBytes buffer size: " + TFormater::int2str(receiveBuffer.size()));
		//if (receiveBuffer.size() > 0) {
		//	int stop = 1;
		//}
	}
	lastReceiveTime = clock() - start;
	_receiveCallback(true, lastReceivedBytes.data(), lastReceivedBytes.size(), nullptr, 0);

	return lastReceivedBytes;
}

U8Vec SerialPortManager::receiveUntil(U8Vec replyEndBytes, int timeOutMS) {
	return receiveBytes(
		"", U8Vec(),
		"", replyEndBytes,
		timeOutMS);
}

std::string SerialPortManager::purgeSendAndWaitForStrReply(
	std::string content,
	std::string replyStartContent,
	std::string replyEndStr, U8Vec replyEndByte,
	int timeOutMS) {
	if (purge() && send(content)) {
		return receiveStr(
			replyStartContent,
			replyEndStr, replyEndByte,
			timeOutMS);
	}
	else {
		return "";
	}
}

U8Vec SerialPortManager::receiveAnyBytes() {
	receiveToBuffer();
	U8Vec ret = receiveBuffer;
	receiveBuffer.clear();

	_receiveCallback(true, ret.data(), ret.size(), nullptr, 0);

	return ret;
	//U8Vec ret;
	//DWORD com_errors = 0;
	//COMSTAT com_stat;
	//DWORD  dwBytesRead;
	//ClearCommError(_sCommCfg.serialHandle, &com_errors, &com_stat);
	//if (com_stat.cbInQue > 0) {
	//	ret.resize(com_stat.cbInQue);
	//	if (!ReadFile(_sCommCfg.serialHandle, &ret[0], ret.size(), &dwBytesRead, NULL)) {
	//		unavailableTimeStart = StopWatch::getTimeSinceStart();
	//	}
	//	else {
	//		unavailableTimeStart = 0;
	//	}
	//}
	//return ret;
}

U8Vec SerialPortManager::getLastReceivedBytes() {
	return lastReceivedBytes;
}

void SerialPortManager::putToBufferStart(std::string content) {
	int sizeBefore = receiveBuffer.size();
	receiveBuffer.resize(content.length() + receiveBuffer.size());
	if (receiveBuffer.size() > 0) {
		memcpy(&receiveBuffer[sizeBefore], &content[0], content.length());
	}
}

bool SerialPortManager::checkReplyStr(std::string expectedReply, int timeout, std::string* replyOut) {
	std::string reply = receiveStr(
		"",
		expectedReply, U8Vec(),
		timeout);
	if (replyOut != nullptr) {
		*replyOut = reply;
	}
	return reply.find(expectedReply) != string::npos;
}

bool SerialPortManager::checkReplyU8(U8Vec expectedReply, int timeout, U8Vec* replyOut) {
	U8Vec reply = receiveBytes(
		"", U8Vec(),
		"", expectedReply,
		timeout);
	if (replyOut != nullptr) {
		*replyOut = reply;
	}
	return (reply.size() > 0) && compareBuffers(&reply[0], reply.size(), &expectedReply[0], expectedReply.size()) >= 0;
}

int SerialPortManager::compareBuffers(void* buffer, size_t bufferLen, void* pattern, size_t patternLen) {
	uint8_t* bufferRef = (uint8_t*)buffer;
	uint8_t* contentRef = (uint8_t*)pattern;
	for (int offset = 0; offset <= (int)bufferLen - (int)patternLen; offset++) {
		uint8_t* bufferWOffset = bufferRef + offset;
		bool flagMatch = true;
		for (int idx = 0; idx < patternLen; idx++) {
			flagMatch &= (bufferWOffset[idx] == contentRef[idx]);
		}
		if (flagMatch) {
			return offset;
		}
	}
	return -1;
}

std::vector<int> SerialPortManager::find(void* buffer, size_t bufferLen, void* pattern, size_t patternLen) {
	std::vector<int> ret;
	uint8_t* bufferRef = (uint8_t*)buffer;
	uint8_t* contentRef = (uint8_t*)pattern;
	for (int offset = 0; offset <= (int)bufferLen - (int)patternLen; offset++) {
		uint8_t* bufferWOffset = bufferRef + offset;
		bool flagMatch = true;
		for (int idx = 0; idx < patternLen; idx++) {
			flagMatch &= (bufferWOffset[idx] == contentRef[idx]);
		}
		if (flagMatch) {
			ret.push_back(offset);
		}
	}
	return ret;
}

time_t SerialPortManager::getUnavailableDuration() {
	if (unavailableTimeStart == 0) {
		return 0;
	}
	else {
		return TStopWatch::getTimeSinceStart() - unavailableTimeStart;
	}
}

int SerialPortManager::reconnect(bool rebootDriverIfNeeded) {
	if (_sCommCfg.flagSimu) {
		return 1;
	}
	TLogger block("Reconnect to serial port");
	TLogger::writeLine(_sCommCfg.portName);
	if (_sCommCfg.portName.length() == 0) {
		TLogger::writeLine("Port not set");
		return 0;
	}
	if (_sCommCfg.serialHandle != NULL) {
		if (!hasError()) {
			return 1;
		}
	}

	TLogger::writeLine("Re-open port");
	CloseHandle(_sCommCfg.serialHandle);
	_sCommCfg.serialHandle = NULL;

	int ret = connectToSelectedPortCore();
	if (ret <= 0) {
		TLogger::writeLine("Cannot re-open port");

		// try reboot driver 

		return ret;
	}
	TLogger::writeLine("Handshake as before");
	if (_sCommCfg.handshake.restart() > 0) {
		return _sCommCfg.handshake.excute(this);
	}
	else {
		return ret;
	}
}

void SerialPortManager::disconnect() {
	if (_sCommCfg.serialHandle != NULL) {
		CloseHandle(_sCommCfg.serialHandle);
	}
	_sCommCfg.serialHandle = NULL;
}

void SerialPortManager::debugTestCallback() {
	char sendText[] = "test_sent_text";
	char sendData[] = "test_sent_data";
	_sentCallback(true,
		sendText, sizeof(sendText),
		sendData, sizeof(sendData));
	char rcvText[] = "test_receive_text";
	char rcvData[] = "test_receive_data";
	_receiveCallback(true,
		rcvText, sizeof(rcvText),
		rcvData, sizeof(rcvData));
}

void SerialPortManager::debugAddReceive(U8Vec dat) {

}

bool SerialPortManager::hasError(string* errorOut) {
	string err = SerialUtilsOnPlatform::hasError(_sCommCfg.serialHandle);
	if (errorOut != nullptr) {
		*errorOut += err;
	}
	return err.length() > 0;
}

int SerialPortManager::connectToSelectedPortCore() {
	_sCommCfg.serialHandle = CreateFile(
		_sCommCfg.portName.c_str(),
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING,
		0,
		NULL);

	if (hasError()) {
		errorHint = "Cannot connect to port. Buzy?";
		disconnect();
		return -1;
	}

	DCB serialParams = { 0 };
	serialParams.DCBlength = sizeof(serialParams);

	if (GetCommState(_sCommCfg.serialHandle, &serialParams) == 0) {
		errorHint = "Cannot get comm state";
		disconnect();
		return -1;
	}

	// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-_dcb
	if (_sCommCfg.baudRate == 115200) {
		serialParams.BaudRate = CBR_115200;
	}
	//serialParams.fDtrControl = DTR_CONTROL_ENABLE;
	//serialParams.fRtsControl = RTS_CONTROL_ENABLE;
	if (_sCommCfg.dataBitNum == 8) {
		serialParams.ByteSize = 8;
	}
	serialParams.StopBits = ONESTOPBIT;
	if (_sCommCfg.parity == 'n' || _sCommCfg.parity == 'N') {
		serialParams.Parity = NOPARITY;
	}
	if (SetCommState(_sCommCfg.serialHandle, &serialParams) == 0) {
		errorHint = "Cannot set comm state";
		disconnect();
		return -1;
	}

	// Set timeouts
	COMMTIMEOUTS timeout = { 0 };
	timeout.ReadIntervalTimeout = 5;
	timeout.ReadTotalTimeoutConstant = 5;
	timeout.ReadTotalTimeoutMultiplier = 5;
	timeout.WriteTotalTimeoutConstant = 5;
	timeout.WriteTotalTimeoutMultiplier = 5;

	if (SetCommTimeouts(_sCommCfg.serialHandle, &timeout) == 0) {
		errorHint = "Cannot set timtouts";
		disconnect();
		return -1;
	}
	purge();
	return 1;
}

//bool SerialPortManager::hasError(HANDLE handle, std::string* errorOut) {
//	if (handle == NULL) {
//		return true;
//	}
//	DWORD dwErrors = 0;
//	COMSTAT comStat;
//	if (ClearCommError(handle, &dwErrors, &comStat) > 0) {
//		// Report error in ClearCommError.
//		return false;
//	}
//
//	// Get error flags.
//	BOOL    fDNS = dwErrors & CE_DNS;
//	BOOL    fIOE = dwErrors & CE_IOE;
//	BOOL    fOOP = dwErrors & CE_OOP;
//	BOOL    fPTO = dwErrors & CE_PTO;
//	BOOL    fMODE = dwErrors & CE_MODE;
//	BOOL    fBREAK = dwErrors & CE_BREAK;
//	BOOL    fFRAME = dwErrors & CE_FRAME;
//	BOOL    fRXOVER = dwErrors & CE_RXOVER;
//	BOOL    fTXFULL = dwErrors & CE_TXFULL;
//	BOOL    fOVERRUN = dwErrors & CE_OVERRUN;
//	BOOL    fRXPARITY = dwErrors & CE_RXPARITY;
//	BOOL opened = (dwErrors == IE_OPEN);
//	return true;
//}

int SerialPortManager::receiveToBuffer() {
	DWORD com_errors = 0;
	COMSTAT com_stat;
	ClearCommError(_sCommCfg.serialHandle, &com_errors, &com_stat);
	if (com_stat.cbInQue > 0) {
		size_t beforeSize = receiveBuffer.size();
		receiveBuffer.resize(beforeSize + com_stat.cbInQue);
		void* pointer = &receiveBuffer[0] + beforeSize;
		DWORD  dwBytesRead;
		if (ReadFile(_sCommCfg.serialHandle, pointer, com_stat.cbInQue, &dwBytesRead, NULL)) {
			unavailableTimeStart = 0;
			return dwBytesRead;
		}
		else {
			unavailableTimeStart = TStopWatch::getTimeSinceStart();
			return -1;
		}
	}
	else {
		unavailableTimeStart = TStopWatch::getTimeSinceStart();
		return 0;
	}
}

bool SerialPortManager::receiveUntilMatchStartEnd(
	int offset,
	void* startKeywordPtr, int startKeywordLen,
	void* endKeywordPtr, int endKeywordLen,
	int timeout, int* matchStartIdxOut, int* matchEndIdxOut) {
	clock_t beginTime = clock();
	int startOffset = receiveUntilMatchBuffer(
		offset,
		startKeywordPtr, startKeywordLen,
		beginTime, timeout);
	if (startOffset >= 0) {
		int endOffset = receiveUntilMatchBuffer(
			startOffset + startKeywordLen,
			endKeywordPtr, endKeywordLen,
			beginTime, timeout);
		if (endOffset >= 0) {
			*matchStartIdxOut = startOffset;
			*matchEndIdxOut = endOffset + endKeywordLen - 1;
			return true;
		}
		else {
			return false;
		}
	}
	else {
		return false;
	}
	return false;
}

int SerialPortManager::receiveUntilMatchBuffer(int startOffset, void* contentIn, size_t contentLen, long startTime, int timeout) {
	if (contentIn == nullptr || contentLen == 0) {
		return startOffset;
	}
	do {
		//SLogger::writeLine("SerialPortManager::receiveUntilMatchBuffer 1");
		int endOffset = receiveUntilMatchBufferLoop(startOffset, contentIn, contentLen);
		//SLogger::writeLine("SerialPortManager::receiveUntilMatchBuffer 2");
		if (endOffset >= startOffset) {
			return endOffset;
		}
		else {
			Sleep(1);
		}
	} while (clock() - startTime < timeout);
	int endOffset = receiveUntilMatchBufferLoop(startOffset, contentIn, contentLen);
	if (endOffset >= startOffset) {
		return endOffset;
	}
	else {
		return -1;
	}
}

int SerialPortManager::receiveUntilMatchBufferLoop(int startOffset, void* contentIn, size_t contentLen) {
	//if (isConnected()) {

	//}
	//else if (receiveBuffer) {

	//}
	DWORD com_errors = 0;
	COMSTAT com_stat;
	DWORD  dwBytesRead;
	ClearCommError(_sCommCfg.serialHandle, &com_errors, &com_stat);
	if (com_stat.cbInQue > 0) {
		if (com_stat.cbInQue > 4095) {
			// overflow
			return -2;
		}
		if (receiveToBuffer() <= 0) {
			return startOffset - 1;
		}
		//uint8_t* buffer = new uint8_t[com_stat.cbInQue];
		//if ((!ReadFile(_sCommCfg.serialHandle, buffer, com_stat.cbInQue, &dwBytesRead, NULL)) || (dwBytesRead == 0)) {
		//	//SLogger::writeLine("SerialPortManager::receiveUntilMatchBufferLoop read fail");
		//	delete[] buffer;
		//	return startOffset - 1;
		//}
		//else {
		//	int lastSize = receiveBuffer.size();
		//	receiveBuffer.resize(lastSize + dwBytesRead);
		//	memcpy(&receiveBuffer[lastSize], buffer, dwBytesRead);
		//	delete[] buffer;
		//}
		if (receiveBuffer.size() <= startOffset) {
			return -1;
		}
		int offset = compareBuffers(
			&receiveBuffer[startOffset], receiveBuffer.size() - startOffset,
			contentIn, contentLen);
		if (offset >= 0) {
			return offset + startOffset;
		}
		else {
			return -1;
		}
	}
	else {
		if (receiveBuffer.size() > 0 && receiveBuffer.size() > startOffset) {
			int offset = compareBuffers(
				&receiveBuffer[startOffset], receiveBuffer.size() - startOffset,
				contentIn, contentLen);
			if (offset >= 0) {
				return offset + startOffset;
			}
			else {
				return -1;
			}
		}
		else {
			return -1;
		}
	}
}

std::string SerialPortManager::extractFromBuffer(int replyStartIdx, int replyEndIdx) {
	char* reply = new char[replyEndIdx - replyStartIdx + 1 + 1];
	//SLogger::writeLine("SerialPortManager::receiveStr 1");
	memcpy(reply, &receiveBuffer[replyStartIdx], replyEndIdx - replyStartIdx + 1);
	reply[replyEndIdx - replyStartIdx + 1] = '\0';
	string replyStr = string(reply);
	delete[] reply;

	if (replyEndIdx == receiveBuffer.size() - 1) {
		receiveBuffer.resize(replyStartIdx);
	}
	else {
		memmove(&receiveBuffer[replyStartIdx], &receiveBuffer[replyEndIdx + 1], receiveBuffer.size() - replyEndIdx - 1);
		receiveBuffer.resize(receiveBuffer.size() - (replyEndIdx - replyStartIdx + 1));
	}

	return replyStr;
}

std::string SerialPortManager::copyFromBuffer(int replyStartIdx, int replyEndIdx) {
	char* reply = new char[replyEndIdx - replyStartIdx + 1 + 1];
	//SLogger::writeLine("SerialPortManager::receiveStr 1");
	memcpy(reply, &receiveBuffer[replyStartIdx], replyEndIdx - replyStartIdx + 1);
	reply[replyEndIdx - replyStartIdx + 1] = '\0';
	string replyStr = string(reply);
	delete[] reply;

	//if (replyEndIdx == receiveBuffer.size() - 1) {
	//	receiveBuffer.resize(replyStartIdx);
	//}
	//else {
	//	memmove(&receiveBuffer[replyStartIdx], &receiveBuffer[replyEndIdx + 1], receiveBuffer.size() - replyEndIdx - 1);
	//	receiveBuffer.resize(receiveBuffer.size() - (replyEndIdx - replyStartIdx + 1));
	//}

	return replyStr;
}

void SerialPortManager::_sentCallback(bool success, const void* log, int logLen, const void* data, int dataLen) {
	if (_callback.sentCallbackMP != NULL) {
		_callback.sentCallbackMP(_callback.referenceIndex, success, log, logLen, data, dataLen);
	}
	//if (_callback.sentCallback != NULL) {
	//	_callback.sentCallback(success, log, logLen, data, dataLen);
	//}
}

void SerialPortManager::_receiveCallback(bool success, const void* log, int logLen, const void* data, int dataLen) {
	if (_callback.receiveCallbackMP != NULL) {
		_callback.receiveCallbackMP(_callback.referenceIndex, success, log, logLen, data, dataLen);
	}
	//if (_callback.receiveCallback != NULL) {
	//	_callback.receiveCallback(success, log, logLen, data, dataLen);
	//}
}